package com.linln.admin.api.controller;

import com.linln.admin.api.validator.ProjectValid;
import com.linln.common.enums.StatusEnum;
import com.linln.common.exception.ResultException;
import com.linln.common.utils.*;
import com.linln.common.vo.ResultVo;
import com.linln.component.excel.ExcelReader;
import com.linln.component.fileUpload.FileUpload;
import com.linln.component.fileUpload.enums.UploadResultEnum;
import com.linln.component.jwt.annotation.IgnorePermissions;
import com.linln.modules.api.domain.Project;
import com.linln.modules.api.enums.ProjectTypeEnum;
import com.linln.modules.api.service.ProjectService;
import com.linln.modules.system.domain.Upload;
import com.linln.modules.system.service.UploadService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author cocia
 * @date 2020/08/03
 */
@Controller
@RequestMapping("/api/project")
public class ProjectController {

    @Autowired
    private ProjectService projectService;
    @Autowired
    private UploadService uploadService;
    private static String PROJ_CODE_FIELD="xh";//excel中项目编号的列名
    private static String PROJ_NAME_FIELD="xmmc";//excel中项目名称的列名

    /**
     * 列表页面
     */
    @GetMapping("/index")
    @RequiresPermissions("api:project:index")
    public String index(Model model, Project project) {

        // 创建匹配器，进行动态查询匹配
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("title", match -> match.contains());

        // 获取数据列表
        Example<Project> example = Example.of(project, matcher);
        Page<Project> list = projectService.getPageList(example);

        // 封装数据
        model.addAttribute("list", list.getContent());
        model.addAttribute("page", list);
        return "/api/project/index";
    }

    /**
     * 跳转到添加页面
     */
    @GetMapping("/add")
    @RequiresPermissions("api:project:add")
    public String toAdd() {
        return "/api/project/add";
    }

    /**
     * 跳转到编辑页面
     */
    @GetMapping("/edit/{id}")
    @RequiresPermissions("api:project:edit")
    public String toEdit(@PathVariable("id") Project project, Model model) {
        model.addAttribute("project", project);
        return "/api/project/add";
    }

    /**
     * 保存添加/修改的数据
     * @param valid 验证对象
     */
    @PostMapping("/save")
    @RequiresPermissions({"api:project:add", "api:project:edit"})
    @ResponseBody
    public ResultVo save(@Validated ProjectValid valid, Project project) {
        // 复制保留无需修改的数据
        if (project.getId() != null) {
            Project beProject = projectService.getById(project.getId());
            EntityBeanUtil.copyProperties(beProject, project);
        }

        // 保存数据
        projectService.save(project);
        return ResultVoUtil.SAVE_SUCCESS;
    }

    /**
     * 跳转到详细页面
     */
    @GetMapping("/detail/{id}")
    @RequiresPermissions("api:project:detail")
    @IgnorePermissions
    public String toDetail(@PathVariable("id") Project project, Model model) {
        model.addAttribute("project",project);
        return "/api/project/detail";
    }

    /**
     * 设置一条或者多条数据的状态
     */
    @RequestMapping("/status/{param}")
    @RequiresPermissions("api:project:status")
    @ResponseBody
    public ResultVo status(
            @PathVariable("param") String param,
            @RequestParam(value = "ids", required = false) List<Long> ids) {
        // 更新状态
        StatusEnum statusEnum = StatusUtil.getStatusEnum(param);
        if (projectService.updateStatus(statusEnum, ids)) {
            return ResultVoUtil.success(statusEnum.getMessage() + "成功");
        } else {
            return ResultVoUtil.error(statusEnum.getMessage() + "失败，请重新操作");
        }
    }

    /**
     * 跳转到详细页面
     */
    @GetMapping("/match")
    @RequiresPermissions("api:project:match")
    @IgnorePermissions
    public String match() {
        return "/api/project/match";
    }
    /**
     * 文件上传
     */
    @PostMapping("/upload")
    @ResponseBody
    public ResultVo uploadPrjFile(@RequestParam("prjFile") MultipartFile multipartFile) {

        // 创建Upload实体对象
        Upload upload = FileUpload.getFile(multipartFile, "/prjFile");
        try {
            String[] types = {
                    "application/vnd.ms-excel",
            };
            if(!FileUpload.isContentType(multipartFile, types)){
                throw new ResultException(UploadResultEnum.NO_FILE_TYPE);
            }

            // 判断文件是否存在
            Upload uploadSha1 = uploadService.getBySha1(FileUpload.getFileSha1(multipartFile));
            if (uploadSha1 != null) {
                return ResultVoUtil.success(uploadSha1);
            }

            FileUpload.transferTo(multipartFile, upload);
            // 将文件信息保存到数据库中
            uploadService.save(upload);
            return ResultVoUtil.success(upload);
        } catch (IOException | NoSuchAlgorithmException e) {
            return ResultVoUtil.error("上传项目匹配文件失败");
        }
    }

    @GetMapping("/excMatch")
    @ResponseBody
    public ResultVo excMatch(
            @RequestParam(value = "upFileId", required = true) String upFileId
    ) {
        Upload upload=uploadService.getById(Long.valueOf(upFileId));
        String basePath=FileUpload.getUploadPath();
//        UploadProjectProperties properties = SpringContextUtil.getBean(UploadProjectProperties.class);
//        String basePath=properties.getAbsolutePath();
        String absUrl= basePath.substring(0,basePath.length()-8)+upload.getPath();
        File file = new File(absUrl);
        FileInputStream fis= null;
        try {
            fis = new FileInputStream(file);
            ExcelReader reader=new ExcelReader();
            boolean isOffice2003=reader.isExcel2003(file.getName());
            List<List<String>> rows = reader.read(fis,isOffice2003);
            Map<String,Integer> fieldMap=this.getFieldMap(rows,0);
            List<Project> inProjects=this.parseProjects(rows,fieldMap);
            String warnMsg=projectService.match(inProjects);
            return ResultVoUtil.success();
        } catch (FileNotFoundException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
            return ResultVoUtil.error("项目匹配失败");
        }
    }

    /**
     * 获取导入字段名称和index的映射关系
     * @return
     */
    private Map<String,Integer> getFieldMap(List<List<String>> rows,int titleRow){
        Map<String,Integer> fieldMap=new HashMap<>();
        List<String> fields = rows.get(titleRow);
        for (int i=0;i<fields.size();i++){
            String field=fields.get(i);
            fieldMap.put(field,i);
        }
        return fieldMap;
    }

    /**
     * 将excel行数据转换成project对象
     * @param rows
     * @return
     */
    private List<Project> parseProjects(List<List<String>> rows,Map<String,Integer> fieldMap)
            throws InvocationTargetException, IllegalAccessException {
        List<Project> projects=new ArrayList<>();
        for (int i=1;i<rows.size();i++){
            List<String> cells=rows.get(i);

            Project project=new Project();
            String projCode=cells.get(fieldMap.get(PROJ_CODE_FIELD));
            project.setProjCode(projCode);
            String projName=cells.get(fieldMap.get(PROJ_NAME_FIELD));
            project.setTitle(projName);
            project.setType(ProjectTypeEnum.BUILDING.getCode());

            Field[] fields = project.getClass().getDeclaredFields(); // 获取实体类的所有属性，返回Field数组
            for (int j = 0; j < fields.length; j++) { // 遍历所有属性
                Field field = fields[j];
                String fieldName = field.getName();
                if(!fieldMap.containsKey(fieldName)) continue;//excel中不存在该字段，则直接跳过

                int cIndex=fieldMap.get(fieldName);
                Object value=cells.get(cIndex);
                this.setField(project,field,value);
//                EntityBeanUtil.setField(project,fieldName,value);
            }
            projects.add(project);
        }
        return projects;
    }

    /**
     * 设置字段属性
     * @param project
     * @param field
     * @param fieldValue
     */
    private void setField(Project project, Field field, Object fieldValue){
        try {
            Class clz=project.getClass();
            String name = field.getName(); // 获取属性的名字
            name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写，方便构造get，set方法
            String type = field.getGenericType().toString(); // 获取属性的类型
            if (type.equals("class java.lang.String")) { // 如果type是类类型，则前面包含"class "，后面跟类名
                if (fieldValue != null) {
                    String value = (String) fieldValue; // 调用getter方法获取属性值
                    Method m = clz.getMethod("set"+name,String.class);
                    m.invoke(project, value);
                }
            }
            if (type.equals("class java.lang.Integer")) {
                if (fieldValue != null) {
                    Integer value = Integer.parseInt(fieldValue.toString());
                    Method m = clz.getMethod("set"+name,Integer.class);
                    m.invoke(project, value);
                }
            }
            if (type.equals("class java.lang.Double")) {
                if (fieldValue != null) {
                    Double value = Double.parseDouble(fieldValue.toString());
                    Method m = clz.getMethod("set"+name,Double.class);
                    m.invoke(project, value);
                }
            }
            if (type.equals("class java.lang.Boolean")) {
                if (fieldValue != null) {
                    Boolean value = Boolean.parseBoolean(fieldValue.toString());
                    Method m = clz.getMethod("set"+name,Boolean.class);
                    m.invoke(project, value);
                }
            }
            if (type.equals("class java.util.Date")) {
                if (fieldValue != null) {
                    Date value = DateUtil.fromString(fieldValue.toString());
                    Method m = clz.getMethod("set"+name,Date.class);
                    m.invoke(project, value);
                }
            }// 如果有需要,可以仿照上面继续进行扩充,再增加对其它类型的判断
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


}